home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / lisp / stk-3.0 / stk-3 / blt-for-STk-3.0 / blt-1.9 / src / bltGrElem.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-07-01  |  39.8 KB  |  1,369 lines

  1. /*
  2.  * bltGrElem.c --
  3.  *
  4.  *    This module implements a elements in the graph widget
  5.  *    for the Tk toolkit.
  6.  *
  7.  * Copyright 1991-1994 by AT&T Bell Laboratories.
  8.  * Permission to use, copy, modify, and distribute this software
  9.  * and its documentation for any purpose and without fee is hereby
  10.  * granted, provided that the above copyright notice appear in all
  11.  * copies and that both that the copyright notice and warranty
  12.  * disclaimer appear in supporting documentation, and that the
  13.  * names of AT&T Bell Laboratories any of their entities not be used
  14.  * in advertising or publicity pertaining to distribution of the
  15.  * software without specific, written prior permission.
  16.  *
  17.  * AT&T disclaims all warranties with regard to this software, including
  18.  * all implied warranties of merchantability and fitness.  In no event
  19.  * shall AT&T be liable for any special, indirect or consequential
  20.  * damages or any damages whatsoever resulting from loss of use, data
  21.  * or profits, whether in an action of contract, negligence or other
  22.  * tortuous action, arising out of or in connection with the use or
  23.  * performance of this software.
  24.  *
  25.  */
  26.  
  27. #include "blt.h"
  28. #include "bltGraph.h"
  29. #include <ctype.h>
  30. #include <X11/Xutil.h>
  31. #include <X11/Xatom.h>
  32.  
  33.  
  34. /*
  35.  * Sun's bundled and unbundled C compilers can't grok static function
  36.  * typedefs (it can handle extern) like
  37.  *
  38.  *     static Tk_OptionParseProc parseProc;
  39.  *      static Tk_OptionPrintProc printProc;
  40.  *
  41.  * Provide forward declarations here:
  42. */
  43.  
  44. static int ParseVector _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp,
  45.     Tk_Window tkwin, char *value, char *widgRec, int offset));
  46. static char *PrintVector _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin,
  47.     char *widgRec, int offset, Tcl_FreeProc **freeProcPtr));
  48. static int ParseCoordPairs _ANSI_ARGS_((ClientData clientData,
  49.     Tcl_Interp *interp, Tk_Window tkwin, char *value, char *widgRec,
  50.     int offset));
  51. static char *PrintCoordPairs _ANSI_ARGS_((ClientData clientData,
  52.     Tk_Window tkwin, char *widgRec, int offset,
  53.     Tcl_FreeProc **freeProcPtr));
  54.  
  55. #include "bltGrElem.h"
  56.  
  57. #define DEF_VECTOR_SIZE    32    /* Starting size of a data vector. */
  58.  
  59. enum DataModes {
  60.     CopyData, AppendData
  61. };
  62. enum DataTypes {
  63.     SingleVector = 1, TwinVectors = 2
  64. };
  65.  
  66. Tk_CustomOption bltXVectorOption =
  67. {
  68.     ParseVector, PrintVector, (ClientData)ANY_X_MASK
  69. };
  70. Tk_CustomOption bltYVectorOption =
  71. {
  72.     ParseVector, PrintVector, (ClientData)ANY_Y_MASK
  73. };
  74.  
  75. Tk_CustomOption bltTwinOption =
  76. {
  77.     ParseCoordPairs, PrintCoordPairs, (ClientData)0
  78. };
  79.  
  80. extern Element *Blt_BarElement();
  81. extern Element *Blt_LineElement();
  82.  
  83.  
  84. /* ----------------------------------------------------------------------
  85.  * Custom option parse and print procedures
  86.  * ----------------------------------------------------------------------
  87.  */
  88.  
  89. /*
  90.  *----------------------------------------------------------------------
  91.  *
  92.  * ConvertExpressions --
  93.  *
  94.  *    Converts strings from an array of numeric expressions into
  95.  *    an array of double precision values.
  96.  *
  97.  * Results:
  98.  *         Returns TCL_OK if sucessful. Otherwise TCL_ERROR will be
  99.  *    returned and interp->result will be filled with an error message.
  100.  *
  101.  *----------------------------------------------------------------------
  102.  */
  103. static int
  104. ConvertExpressions(interp, exprArr, numExpr, dataArr, start, step)
  105.     Tcl_Interp *interp;        /* Interpreter to report error back to */
  106.     char *exprArr[];        /* Array of expression strings */
  107.     int numExpr;        /* Number of expressions in array */
  108.     double dataArr[];
  109.     int start, step;        /* Starting point and step */
  110. {
  111.     register int i, count;
  112.     double x;
  113.  
  114.     /*
  115.      * Parse and evaluate the list of numeric expressions into an
  116.      * array of floating point numbers.
  117.      */
  118.     for (count = 0, i = start; i < numExpr; i += step, count++) {
  119.     if (Tcl_ExprDouble(interp, exprArr[i], &x) != TCL_OK) {
  120.         return TCL_ERROR;
  121.     }
  122.     dataArr[count] = x;
  123.     }
  124.     return TCL_OK;
  125. }
  126.  
  127. /*
  128.  *----------------------------------------------------------------------
  129.  *
  130.  * GetDataLimits --
  131.  *
  132.  *    Find the minimum, positive minumum, and maximum values in a
  133.  *    given vector and store the results in the vector structure.
  134.  *
  135.  * Results:
  136.  *         None.
  137.  *
  138.  * Side Effects:
  139.  *    Minimum, positive minimum, and maximum values are stored in
  140.  *    the vector.
  141.  *
  142.  *----------------------------------------------------------------------
  143.  */
  144. static void
  145. GetDataLimits(vecPtr)
  146.     register Vector *vecPtr;
  147. {
  148.     register int i;
  149.  
  150.     /*
  151.      * Find the minimum, positive minimum, and maximum values in the
  152.      * array.
  153.      */
  154.     vecPtr->min = vecPtr->max = vecPtr->data[0];
  155.     vecPtr->logMin = Blt_posInfinity;
  156.     for (i = 0; i < vecPtr->length; i++) {
  157.     if ((vecPtr->data[i] > 0.0) && (vecPtr->data[i] < vecPtr->logMin)) {
  158.         vecPtr->logMin = vecPtr->data[i];
  159.     }
  160.     if (vecPtr->data[i] < vecPtr->min) {
  161.         vecPtr->min = vecPtr->data[i];
  162.     } else if (vecPtr->data[i] > vecPtr->max) {
  163.         vecPtr->max = vecPtr->data[i];
  164.     }
  165.     }
  166. }
  167.  
  168. /*
  169.  *----------------------------------------------------------------------
  170.  *
  171.  * AppendVector --
  172.  *
  173.  *    Appends or overwrites a given vector with numeric expressions.
  174.  *
  175.  * Results:
  176.  *         A standard Tcl result.
  177.  *
  178.  * Side Effects:
  179.  *    Memory is allocated for the new vector values.
  180.  *
  181.  *----------------------------------------------------------------------
  182.  */
  183. static int
  184. AppendVector(interp, vecPtr, exprArr, numExpr, start, type, mode)
  185.     Tcl_Interp *interp;        /* Interpreter to report error back to */
  186.     Vector *vecPtr;        /* Vector to be filled */
  187.     char *exprArr[];        /* Array of expression strings */
  188.     int numExpr;        /* Number of expressions in array */
  189.     int start;            /* Starting point */
  190.     enum DataTypes type;    /* Type of data (pairs or single vector) */
  191.     enum DataModes mode;    /* Append or overwrite current data */
  192. {
  193.     unsigned int arraySize;
  194.     unsigned int needed;
  195.     double *dataArr;
  196.     unsigned int offset;
  197.     int step;
  198.  
  199.     if (numExpr < 1) {
  200.     if (vecPtr->data != NULL) {
  201.         free((char *)vecPtr->data);
  202.     }
  203.     vecPtr->data = NULL;
  204.     vecPtr->size = 0;
  205.     vecPtr->length = 0;
  206.     return TCL_OK;
  207.     }
  208.     step = (int)type;
  209.     offset = (mode == AppendData) ? vecPtr->length : 0;
  210.     needed = (numExpr / step) + offset;
  211.  
  212.     /*
  213.      * Keep doubling the size of the array until we fit the number
  214.      * needed
  215.      */
  216.     arraySize = vecPtr->size;
  217.     if (arraySize == 0) {
  218.     arraySize = DEF_VECTOR_SIZE;
  219.     }
  220.     while (needed > arraySize) {
  221.     arraySize += arraySize;
  222.     }
  223.  
  224.     dataArr = (double *)calloc(arraySize, sizeof(double));
  225.     if (dataArr == NULL) {
  226.     interp->result = "can't allocate data vector";
  227.     return TCL_ERROR;
  228.     }
  229.     if (offset > 0) {        /* Copy previous contents */
  230.     memcpy((char *)dataArr, (char *)vecPtr->data, offset * sizeof(double));
  231.     }
  232.     if (ConvertExpressions(interp, exprArr, numExpr,
  233.         &(dataArr[offset]), start, step) != TCL_OK) {
  234.     free((char *)dataArr);
  235.     return TCL_ERROR;
  236.     }
  237.     if (vecPtr->data != NULL) {
  238.     free((char *)vecPtr->data);
  239.     }
  240.     vecPtr->data = dataArr;
  241.     vecPtr->length = needed;
  242.     vecPtr->size = arraySize;
  243.     GetDataLimits(vecPtr);
  244.     return TCL_OK;
  245. }
  246.  
  247. /*
  248.  *----------------------------------------------------------------------
  249.  *
  250.  * ParseVector --
  251.  *
  252.  *    Given a Tcl list of numeric expression representing the element
  253.  *    values, convert into an array of double precision values. In
  254.  *    addition, the minimum and maximum values are saved.  Since
  255.  *    elastic values are allow (values which translate to the
  256.  *    min/max of the graph), we must try to get the non-elastic
  257.  *    minimum and maximum.
  258.  *
  259.  * Results:
  260.  *    The return value is a standard Tcl result.  The vector is passed
  261.  *    back via the vecPtr.
  262.  *
  263.  *----------------------------------------------------------------------
  264.  */
  265. /*ARGSUSED*/
  266. static int
  267. ParseVector(clientData, interp, tkwin, value, widgRec, offset)
  268.     ClientData clientData;    /* Type of axis vector to fill */
  269.     Tcl_Interp *interp;        /* Interpreter to send results back to */
  270.     Tk_Window tkwin;        /* not used */
  271.     char *value;        /* Tcl list of expressions */
  272.     char *widgRec;        /* Element record */
  273.     int offset;            /* Offset of vector in Element record */
  274. {
  275.     Element *elemPtr = (Element *)(widgRec + offset);
  276.     register Vector *vecPtr;
  277.     int flags = (int)clientData;
  278.     int numExprs;
  279.     char **exprArr = NULL;
  280.  
  281.     vecPtr = (flags & ANY_X_MASK) ? &(elemPtr->x) : &(elemPtr->y);
  282.  
  283.     /* Split the list of expressions and check the values */
  284.     if (Tcl_SplitList(interp, value, &numExprs, &exprArr) != TCL_OK) {
  285.     return TCL_ERROR;
  286.     }
  287.     if (numExprs >= 65535) {
  288.     interp->result = "vector is too large";    /* XDrawLines limit */
  289.     goto error;
  290.     }
  291.     if (AppendVector(interp, vecPtr, exprArr, numExprs, 0, SingleVector,
  292.         CopyData) != TCL_OK) {
  293.     goto error;
  294.     }
  295.     free((char *)exprArr);
  296.     return TCL_OK;
  297.  
  298.   error:
  299.     /* Clean up, release allocated storage */
  300.     if (exprArr) {
  301.     free((char *)exprArr);
  302.     }
  303.     return TCL_ERROR;
  304. }
  305.  
  306. /*
  307.  *----------------------------------------------------------------------
  308.  *
  309.  * PrintVector --
  310.  *
  311.  *    Convert the vector of floating point values into a Tcl list.
  312.  *
  313.  * Results:
  314.  *    The string representation of the vector is returned.
  315.  *
  316.  *----------------------------------------------------------------------
  317.  */
  318. /*ARGSUSED*/
  319. static char *
  320. PrintVector(clientData, tkwin, widgRec, offset, freeProcPtr)
  321.     ClientData clientData;    /* Type of axis vector to print */
  322.     Tk_Window tkwin;        /* not used */
  323.     char *widgRec;        /* Element record */
  324.     int offset;            /* Offset of vector in Element record */
  325.     Tcl_FreeProc **freeProcPtr;    /* Memory deallocation scheme to use */
  326. {
  327.     Element *elemPtr = (Element *)(widgRec + offset);
  328.     register Vector *vecPtr;
  329.     int flags = (int)clientData;
  330.     register int i;
  331.     char *result;
  332.     Tcl_DString buffer;
  333.     char string[TCL_DOUBLE_SPACE + 1];
  334.  
  335.     vecPtr = (flags & ANY_X_MASK) ? &(elemPtr->x) : &(elemPtr->y);
  336.     if (vecPtr->length == 0) {
  337.     return "";
  338.     }
  339.     Tcl_DStringInit(&buffer);
  340.     for (i = 0; i < vecPtr->length; i++) {
  341.     Tcl_PrintDouble(elemPtr->interp, vecPtr->data[i], string);
  342.     Tcl_DStringAppendElement(&buffer, string);
  343.     }
  344.     result = Tcl_DStringValue(&buffer);
  345.     result = strdup(result);
  346.     *freeProcPtr = (Tcl_FreeProc *)free;
  347.     Tcl_DStringFree(&buffer);
  348.     return (result);
  349. }
  350.  
  351. /*
  352.  *----------------------------------------------------------------------
  353.  *
  354.  * ParseCoordPairs --
  355.  *
  356.  *    This procedure is like ParseVector except that it
  357.  *    interprets the list of numeric expressions as X Y coordinate
  358.  *    pairs.  The minimum and maximum for both the X and Y vectors are
  359.  *    determined.
  360.  *
  361.  * Results:
  362.  *    The return value is a standard Tcl result.  The vectors are passed
  363.  *    back via the widget record (elemPtr).
  364.  *
  365.  *----------------------------------------------------------------------
  366.  */
  367. /*ARGSUSED*/
  368. static int
  369. ParseCoordPairs(clientData, interp, tkwin, value, widgRec, offset)
  370.     ClientData clientData;    /* not used */
  371.     Tcl_Interp *interp;        /* Interpreter to send results back to */
  372.     Tk_Window tkwin;        /* not used */
  373.     char *value;        /* Tcl list of numeric expressions */
  374.     char *widgRec;        /* Element record */
  375.     int offset;            /* not used */
  376. {
  377.     Element *elemPtr = (Element *)widgRec;
  378.     int numExprs;
  379.     char **exprArr = NULL;
  380.  
  381.     /* Split the list of numbers and check the values */
  382.     if (Tcl_SplitList(interp, value, &numExprs, &exprArr) != TCL_OK) {
  383.     return TCL_ERROR;
  384.     }
  385.     if (numExprs >= 131070) {
  386.     interp->result = "vector is too large";    /* XDrawLines limit */
  387.     goto error;
  388.     } else if (numExprs & 1) {
  389.     interp->result = "odd number of values in -xydata option";
  390.     goto error;
  391.     }
  392.     if (AppendVector(interp, &(elemPtr->x), exprArr, numExprs, 0,
  393.         TwinVectors, CopyData) != TCL_OK) {
  394.     goto error;
  395.     }
  396.     if (AppendVector(interp, &(elemPtr->y), exprArr, numExprs, 1,
  397.         TwinVectors, CopyData) != TCL_OK) {
  398.     goto error;
  399.     }
  400.     free((char *)exprArr);
  401.     return TCL_OK;
  402.   error:
  403.     /* Clean up, release allocated storage */
  404.     if (exprArr) {
  405.     free((char *)exprArr);
  406.     }
  407.     return TCL_ERROR;
  408. }
  409.  
  410. /*
  411.  *----------------------------------------------------------------------
  412.  *
  413.  * PrintCoordPairs --
  414.  *
  415.  *    Convert pairs of floating point values in the X and Y arrays
  416.  *    into a Tcl list.
  417.  *
  418.  * Results:
  419.  *    The return value is a string (Tcl list).
  420.  *
  421.  *----------------------------------------------------------------------
  422.  */
  423. /*ARGSUSED*/
  424. static char *
  425. PrintCoordPairs(clientData, tkwin, widgRec, offset, freeProcPtr)
  426.     ClientData clientData;    /* not used */
  427.     Tk_Window tkwin;        /* not used */
  428.     char *widgRec;        /* Element information record */
  429.     int offset;            /* not used */
  430.     Tcl_FreeProc **freeProcPtr;    /* Memory deallocation scheme to use */
  431. {
  432.     register Element *elemPtr = (Element *)widgRec;
  433.     register int i;
  434.     char *result;
  435.     int length;
  436.     char string[TCL_DOUBLE_SPACE + 1];
  437.     Tcl_DString buffer;
  438.  
  439.     result = "";
  440.     if ((elemPtr->x.length < 1) || (elemPtr->y.length < 1)) {
  441.     return result;
  442.     }
  443.     Tcl_DStringInit(&buffer);
  444.     length = BLT_MIN(elemPtr->x.length, elemPtr->y.length);
  445.     for (i = 0; i < length; i++) {
  446.     Tcl_PrintDouble(elemPtr->interp, elemPtr->x.data[i], string);
  447.     Tcl_DStringAppendElement(&buffer, string);
  448.     Tcl_PrintDouble(elemPtr->interp, elemPtr->y.data[i], string);
  449.     Tcl_DStringAppendElement(&buffer, string);
  450.     }
  451.     result = Tcl_DStringValue(&buffer);
  452.     result = strdup(result);
  453.     *freeProcPtr = (Tcl_FreeProc *)free;
  454.     Tcl_DStringFree(&buffer);
  455.     return (result);
  456. }
  457.  
  458. /*
  459.  * Generic element routines:
  460.  */
  461. /*
  462.  *----------------------------------------------------------------------
  463.  *
  464.  * CreateElement --
  465.  *
  466.  *    Add a new element to the graph.
  467.  *
  468.  * Results:
  469.  *    The return value is a standard Tcl result.
  470.  *
  471.  *----------------------------------------------------------------------
  472.  */
  473. static int
  474. CreateElement(graphPtr, interp, argc, argv)
  475.     Graph *graphPtr;
  476.     Tcl_Interp *interp;
  477.     int argc;
  478.     char **argv;
  479. {
  480.     register Element *elemPtr;
  481.     Tcl_HashEntry *entryPtr;
  482.     Blt_ListEntry *listPtr;
  483.     int dummy;
  484.  
  485.     if (argc < 4) {
  486.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  487.         " element create name ?options?\"", (char *)NULL);
  488.     return TCL_ERROR;
  489.     }
  490.     entryPtr = Tcl_FindHashEntry(&(graphPtr->elemTable), argv[3]);
  491.     if (entryPtr != NULL) {
  492.     Tcl_AppendResult(interp, "element \"", argv[3],
  493.         "\" already exists in \"", argv[0], "\"", (char *)NULL);
  494.     return TCL_ERROR;
  495.     }
  496.     elemPtr = NULL;
  497.     if (graphPtr->type == BAR_ELEM_TYPE) {
  498.     elemPtr = Blt_BarElement();
  499.     } else if (graphPtr->type == LINE_ELEM_TYPE) {
  500.     elemPtr = Blt_LineElement();
  501.     }
  502.     if (elemPtr == NULL) {
  503.     Tcl_AppendResult(interp, "can't create element \"", argv[3], "\"",
  504.         (char *)NULL);
  505.     return TCL_ERROR;
  506.     }
  507.     elemPtr->interp = interp;
  508.     elemPtr->id = Tk_GetUid(argv[3]);
  509.     elemPtr->label = strdup(argv[3]);
  510.     elemPtr->axisFlags = STD_AXES_MASK;
  511.     elemPtr->x.size = elemPtr->y.size = 0;
  512.     elemPtr->x.length = elemPtr->y.length = 0;
  513.     elemPtr->activeArr = elemPtr->staticArr;
  514.     elemPtr->numActivePoints = 0;
  515.  
  516.     if (Tk_ConfigureWidget(interp, graphPtr->tkwin, elemPtr->configSpecs,
  517.         argc - 4, argv + 4, (char *)elemPtr, 0) != TCL_OK) {
  518.     (*elemPtr->destroyProc) (graphPtr, elemPtr);
  519.     return TCL_ERROR;
  520.     }
  521.     entryPtr = Tcl_CreateHashEntry(&(graphPtr->elemTable),
  522.     (char *)elemPtr->id, &dummy);
  523.     Tcl_SetHashValue(entryPtr, (ClientData)elemPtr);
  524.     (*elemPtr->configProc) (graphPtr, elemPtr);
  525.     listPtr = Blt_CreateListEntry(elemPtr->id);
  526.     Blt_SetListValue(listPtr, elemPtr);
  527.     Blt_LinkListAfter(&(graphPtr->elemList), listPtr,
  528.     (Blt_ListEntry *)NULL);
  529.     elemPtr->mapped = 1;
  530.     elemPtr->flags |= LAYOUT_NEEDED;
  531.     graphPtr->flags |= DIRTY;
  532.     Blt_ComputeAxes(graphPtr);
  533.     Blt_RedrawGraph(graphPtr);
  534.     return TCL_OK;
  535. }
  536.  
  537. /*
  538.  *----------------------------------------------------------------------
  539.  *
  540.  * ConfigureElement --
  541.  *
  542.  *    Sets the element specifications by the given the command line
  543.  *    arguments and calls the element specification configuration
  544.  *    routine. If zero or one command line options are given, only
  545.  *    information about the option(s) is returned in interp->result.
  546.  *      If the element configuration has changed and the element is
  547.  *    currently displayed, the axis limits are updated and recomputed.
  548.  *
  549.  * Results:
  550.  *    The return value is a standard Tcl result.
  551.  *
  552.  * Side Effects:
  553.  *    Graph will be redrawn to reflect the new display list.
  554.  *
  555.  *----------------------------------------------------------------------
  556.  */
  557. static int
  558. ConfigureElement(graphPtr, interp, argc, argv)
  559.     Graph *graphPtr;
  560.     Tcl_Interp *interp;
  561.     int argc;
  562.     char *argv[];
  563. {
  564.     Tcl_HashEntry *entryPtr;
  565.     Element *elemPtr;
  566.     int flags = TK_CONFIG_ARGV_ONLY;
  567.     int result;
  568.  
  569.     if (argc < 4) {
  570.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  571.         " element configure name ?options?\"", (char *)NULL);
  572.     return TCL_ERROR;
  573.     }
  574.     entryPtr = Tcl_FindHashEntry(&(graphPtr->elemTable), argv[3]);
  575.     if (entryPtr == NULL) {
  576.     Tcl_AppendResult(interp, "can't find element \"", argv[3],
  577.         "\" in \"", argv[0], "\"", (char *)NULL);
  578.     return TCL_ERROR;
  579.     }
  580.     elemPtr = (Element *)Tcl_GetHashValue(entryPtr);
  581.     if (argc == 4) {
  582.     return (Tk_ConfigureInfo(interp, graphPtr->tkwin, elemPtr->configSpecs,
  583.         (char *)elemPtr, (char *)NULL, flags));
  584.     } else if (argc == 5) {
  585.     return (Tk_ConfigureInfo(interp, graphPtr->tkwin, elemPtr->configSpecs,
  586.         (char *)elemPtr, argv[4], flags));
  587.     }
  588.     result = Tk_ConfigureWidget(interp, graphPtr->tkwin, elemPtr->configSpecs,
  589.     argc - 4, argv + 4, (char *)elemPtr, flags);
  590.     if ((*elemPtr->configProc) (graphPtr, elemPtr) != TCL_OK) {
  591.     return TCL_ERROR;
  592.     }
  593.     if (result != TCL_OK) {
  594.     return TCL_ERROR;
  595.     }
  596.     elemPtr->flags |= LAYOUT_NEEDED;
  597.     graphPtr->flags |= LAYOUT_NEEDED;
  598.     if (elemPtr->mapped) {
  599.     graphPtr->flags |= (DIRTY | REFRESH);
  600.     }
  601.     /* Layout entire graph if specific element options change */
  602.     if (Blt_OptionChanged(elemPtr->configSpecs, "-label", (char *)NULL)) {
  603.     graphPtr->flags |= LAYOUT_ALL;
  604.     }
  605.     Blt_ComputeAxes(graphPtr);
  606.     Blt_RedrawGraph(graphPtr);
  607.     return TCL_OK;
  608. }
  609.  
  610. /*
  611.  *----------------------------------------------------------------------
  612.  *
  613.  * AppendElement --
  614.  *
  615.  *    Given a Tcl list of numeric expression representing the element
  616.  *    values, convert into an array of double precision values. In
  617.  *    addition, the minimum and maximum values are saved.  Since
  618.  *    elastic values are allow (values which translate to the
  619.  *    min/max of the graph), we must try to get the non-elastic
  620.  *    minimum and maximum.
  621.  *
  622.  * Results:
  623.  *    The return value is a standard Tcl result.  The vector is passed
  624.  *    back via the vecPtr.
  625.  *
  626.  *----------------------------------------------------------------------
  627.  */
  628. static int
  629. AppendElement(graphPtr, interp, argc, argv)
  630.     Graph *graphPtr;
  631.     Tcl_Interp *interp;
  632.     int argc;
  633.     char **argv;
  634. {
  635.     register Vector *vecPtr;
  636.     int numExprs;
  637.     char **exprArr = NULL;
  638.     int result = TCL_ERROR;
  639.     unsigned int arraySize;
  640.     register Element *elemPtr;
  641.     Tcl_HashEntry *entryPtr;
  642.  
  643.     if (argc != 5) {
  644.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  645.         " element append name coords\"", NULL);
  646.     return TCL_ERROR;
  647.     }
  648.     entryPtr = Tcl_FindHashEntry(&(graphPtr->elemTable), argv[3]);
  649.     if (entryPtr == NULL) {
  650.     Tcl_AppendResult(interp, "can't find element \"", argv[3],
  651.         "\" in \"", argv[0], "\"", (char *)NULL);
  652.     return TCL_ERROR;
  653.     }
  654.     elemPtr = (Element *)Tcl_GetHashValue(entryPtr);
  655.  
  656.     /* Split the list of expressions and check the values */
  657.  
  658.     if (Tcl_SplitList(interp, argv[4], &numExprs, &exprArr) != TCL_OK) {
  659.     return TCL_ERROR;
  660.     }
  661.     if (numExprs < 2) {
  662.     interp->result = "too few numeric expressions in coordinate pair list";
  663.     goto error;
  664.     }
  665.     if (numExprs & 1) {
  666.     interp->result = "odd number of expressions in coordinate pair list";
  667.     goto error;
  668.     }
  669.     /*
  670.      * Check both x and y vectors for XDrawLines size limit (64K)
  671.      */
  672.     vecPtr = &(elemPtr->x);
  673.     arraySize = (numExprs / 2) + vecPtr->length;
  674.     if (arraySize >= 65535) {
  675.     interp->result = "x-coordinate vector is too large";
  676.     goto error;
  677.     }
  678.     if (AppendVector(interp, vecPtr, exprArr, numExprs, 0,
  679.         TwinVectors, AppendData) != TCL_OK) {
  680.     goto error;
  681.     }
  682.     vecPtr = &(elemPtr->y);
  683.     arraySize = (numExprs / 2) + vecPtr->length;
  684.     if (arraySize >= 65535) {
  685.     interp->result = "y-coordinate vector is too large";
  686.     goto error;
  687.     }
  688.     if (AppendVector(interp, vecPtr, exprArr, numExprs, 1,
  689.         TwinVectors, AppendData) != TCL_OK) {
  690.     goto error;
  691.     }
  692.     result = TCL_OK;
  693.     elemPtr->flags |= LAYOUT_NEEDED;    /* Re-generate screen points */
  694.     graphPtr->flags |= DIRTY;
  695.     Blt_ComputeAxes(graphPtr);
  696.     Blt_RedrawGraph(graphPtr);
  697.   error:
  698.     if (exprArr != NULL) {
  699.     free((char *)exprArr);
  700.     }
  701.     return result;
  702. }
  703.  
  704. /*
  705.  *----------------------------------------------------------------------
  706.  *
  707.  * DeleteElements --
  708.  *
  709.  *    Delete the named elements from the graph.
  710.  *
  711.  * Results:
  712.  *    TCL_ERROR is returned if any of the named elements can not be found.
  713.  *    Otherwise TCL_OK is returned;
  714.  *
  715.  * Side Effects:
  716.  *    If the element is currently mapped, the plotting area of the
  717.  *    graph is redrawn. Memory and resources allocated by the elements
  718.  *    are released.
  719.  *
  720.  *----------------------------------------------------------------------
  721.  */
  722. static int
  723. DeleteElements(graphPtr, interp, argc, argv)
  724.     Graph *graphPtr;        /* Graph widget */
  725.     Tcl_Interp *interp;
  726.     int argc;            /* Number of element names */
  727.     char **argv;        /* List of element names */
  728. {
  729.     register Element *elemPtr;
  730.     Tcl_HashEntry *entryPtr;
  731.     Blt_ListEntry *listPtr;
  732.     register int i;
  733.     int count;
  734.  
  735.     if (argc < 3) {
  736.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  737.         " element delete name ?name...?\"", (char *)NULL);
  738.     return TCL_ERROR;
  739.     }
  740.     count = 0;
  741.     for (i = 3; i < argc; i++) {
  742.     entryPtr = Tcl_FindHashEntry(&(graphPtr->elemTable), argv[i]);
  743.     if (entryPtr == NULL) {
  744.         Tcl_AppendResult(interp, "can't find element \"", argv[i],
  745.         "\" in \"", argv[0], (char *)NULL);
  746.         return TCL_ERROR;
  747.     }
  748.     elemPtr = (Element *)Tcl_GetHashValue(entryPtr);
  749.     Tcl_DeleteHashEntry(entryPtr);
  750.  
  751.     listPtr = Blt_FindListEntry(&(graphPtr->elemList), argv[i]);
  752.     if (listPtr != NULL) {
  753.         Blt_DeleteListEntry(&(graphPtr->elemList), listPtr);
  754.         count++;
  755.     }
  756.     /*
  757.      * Call the element's own destructor to release the memory and
  758.      * resources allocated for it.
  759.      */
  760.     (*elemPtr->destroyProc) (graphPtr, elemPtr);
  761.     }
  762.     if (count > 0) {
  763.     Blt_ComputeAxes(graphPtr);
  764.     Blt_RedrawGraph(graphPtr);
  765.     }
  766.     return TCL_OK;
  767. }
  768.  
  769. /*
  770.  *----------------------------------------------------------------------
  771.  *
  772.  * ElementNames --
  773.  *
  774.  *    Runs through the given list of element entries and builds a
  775.  *    Tcl list of element names.  This procedure is used in the
  776.  *    "names" and "show" commands.
  777.  *
  778.  * Results:
  779.  *    The return value is a standard Tcl result.
  780.  *    interp->result contains the list of element names.
  781.  *
  782.  *----------------------------------------------------------------------
  783.  */
  784. static int
  785. ElementNames(graphPtr, interp, argc, argv)
  786.     Graph *graphPtr;
  787.     Tcl_Interp *interp;
  788.     int argc;
  789.     char **argv;
  790. {
  791.     register Element *elemPtr;
  792.     register Tcl_HashEntry *entryPtr;
  793.     Tcl_HashSearch cursor;
  794.  
  795.     if ((argc < 3) || (argc > 4)) {
  796.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  797.         " element names ?pattern?", (char *)NULL);
  798.     return TCL_ERROR;
  799.     }
  800.     for (entryPtr = Tcl_FirstHashEntry(&(graphPtr->elemTable), &cursor);
  801.     entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&cursor)) {
  802.     elemPtr = (Element *)Tcl_GetHashValue(entryPtr);
  803.     if ((argc == 3) || Tcl_StringMatch(elemPtr->id, argv[3])) {
  804.         Tcl_AppendElement(interp, elemPtr->id);
  805.     }
  806.     }
  807.     return TCL_OK;
  808. }
  809.  
  810. /*
  811.  *----------------------------------------------------------------------
  812.  *
  813.  * ResetDisplayList --
  814.  *
  815.  *    Given a Tcl list of element names, this procedure rebuilds the
  816.  *    display list, ignoring invalid element names. This list describes
  817.  *    not only only which elements to draw, but in what order.  This is
  818.  *    only important for bar and pie charts.
  819.  *
  820.  * Results:
  821.  *    The return value is a standard Tcl result.  Only if the Tcl list
  822.  *    cannot be split, a TCL_ERROR is returned and interp->result contains
  823.  *    an error message.
  824.  *
  825.  * Side effects:
  826.  *    The graph is eventually redrawn using the new display list.
  827.  *
  828.  *----------------------------------------------------------------------
  829.  */
  830. static int
  831. ResetDisplayList(graphPtr, newList)
  832.     Graph *graphPtr;        /* Graph widget record */
  833.     char *newList;        /* Tcl list of element names */
  834. {
  835.     int numNames;        /* Number of names found in Tcl name list */
  836.     char **nameArr;        /* Broken out array of element names */
  837.     Blt_ListEntry *listPtr;
  838.     Tcl_HashEntry *hashPtr;
  839.     Tcl_HashSearch cursor;
  840.     register int count;
  841.     Element *elemPtr;        /* Element information record */
  842.  
  843.     if (Tcl_SplitList(graphPtr->interp, newList, &numNames,
  844.         &nameArr) != TCL_OK) {
  845.     Tcl_AppendResult(graphPtr->interp, "can't split name list \"", newList,
  846.         "\"", (char *)NULL);
  847.     return TCL_ERROR;
  848.     }
  849.     /* Clear the display list and mark all elements unmapped. */
  850.     Blt_ClearList(&(graphPtr->elemList));
  851.     for (hashPtr = Tcl_FirstHashEntry(&(graphPtr->elemTable), &cursor);
  852.     hashPtr != NULL; hashPtr = Tcl_NextHashEntry(&cursor)) {
  853.     elemPtr = (Element *)Tcl_GetHashValue(hashPtr);
  854.     elemPtr->mapped = 0;
  855.     }
  856.  
  857.     /*
  858.      * Rebuild the display list, checking that each name it exists
  859.      * (we're currently ignoring invalid element names).
  860.      */
  861.     for (count = 0; count < numNames; count++) {
  862.     hashPtr = Tcl_FindHashEntry(&(graphPtr->elemTable), nameArr[count]);
  863.     if (hashPtr != NULL) {
  864.         elemPtr = (Element *)Tcl_GetHashValue(hashPtr);
  865.         elemPtr->mapped = 1;
  866.         listPtr = Blt_CreateListEntry(elemPtr->id);
  867.         if (listPtr == NULL) {
  868.         free((char *)nameArr);
  869.         return TCL_ERROR;
  870.         }
  871.         Blt_SetListValue(listPtr, elemPtr);
  872.         Blt_LinkListAfter(&(graphPtr->elemList), listPtr,
  873.         (Blt_ListEntry *)NULL);
  874.     }
  875.     }
  876.     free((char *)nameArr);
  877.     graphPtr->flags |= (DIRTY | LAYOUT_ALL);
  878.     Blt_ComputeAxes(graphPtr);
  879.     Blt_RedrawGraph(graphPtr);
  880.     return TCL_OK;
  881. }
  882.  
  883. /*
  884.  *----------------------------------------------------------------------
  885.  *
  886.  * ShowElements --
  887.  *
  888.  *    Displays or rebuilds the element display list.
  889.  *
  890.  * Results:
  891.  *    The return value is a standard Tcl result.
  892.  *    interp->result contains the list of element names.
  893.  *
  894.  *----------------------------------------------------------------------
  895.  */
  896. static int
  897. ShowElements(graphPtr, interp, argc, argv)
  898.     Graph *graphPtr;
  899.     Tcl_Interp *interp;
  900.     int argc;
  901.     char **argv;
  902. {
  903.     register Element *elemPtr;
  904.     register Blt_ListEntry *entryPtr;
  905.  
  906.     if ((argc < 3) || (argc > 4)) {
  907.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  908.         " element show ?nameList?\"", (char *)NULL);
  909.     return TCL_ERROR;
  910.     }
  911.     if (argc == 4) {
  912.     ResetDisplayList(graphPtr, argv[3]);
  913.     }
  914.     for (entryPtr = Blt_FirstListEntry(&(graphPtr->elemList));
  915.     entryPtr != NULL; entryPtr = Blt_NextListEntry(entryPtr)) {
  916.     elemPtr = (Element *)Blt_GetListValue(entryPtr);
  917.     Tcl_AppendElement(interp, (char *)elemPtr->id);
  918.     }
  919.     return TCL_OK;
  920. }
  921.  
  922. /*
  923.  *----------------------------------------------------------------------
  924.  *
  925.  * ClosestElement --
  926.  *
  927.  *    Find the element closest to the window coordinates specified.
  928.  *
  929.  * Results:
  930.  *    Returns TCL_OK if no errors occured. The result field of the
  931.  *    interpreter may contain a list containing the element name,
  932.  *    the index of the closest point, and the x and y graph coordinates
  933.  *    of the point is stored.  If an error occurred, returns TCL_ERROR
  934.  *    and an error message is left in interp->result.
  935.  *
  936.  *----------------------------------------------------------------------
  937.  */
  938. static int
  939. ClosestElement(graphPtr, interp, argc, argv)
  940.     Graph *graphPtr;        /* Graph widget */
  941.     Tcl_Interp *interp;
  942.     int argc;            /* Number of element names */
  943.     char **argv;        /* List of element names */
  944. {
  945.     register Element *elemPtr;
  946.     int x, y;
  947.     ClosestPoint min, elem;
  948.  
  949.     if (argc < 5) {
  950.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  951.         " element closest winX winY ?elements...?\"", (char *)NULL);
  952.     return TCL_ERROR;
  953.     }
  954.     if ((Tk_GetPixels(interp, graphPtr->tkwin, argv[3], &x) != TCL_OK) ||
  955.     (Tk_GetPixels(interp, graphPtr->tkwin, argv[4], &y) != TCL_OK)) {
  956.     Tcl_AppendResult(interp, ": bad window coordinates", (char *)NULL);
  957.     return TCL_ERROR;
  958.     }
  959.     min.dist = Blt_posInfinity;
  960.  
  961.     if (argc > 5) {
  962.     Tcl_HashEntry *entryPtr;
  963.     register int i;
  964.  
  965.     /*
  966.      * Search for closest point in set of elements listed. In case
  967.      * of two or more elements have the same closest point, pick
  968.      * the first element.
  969.      */
  970.     for (i = 5; i < argc; i++) {    /* Element name list supplied */
  971.         entryPtr = Tcl_FindHashEntry(&(graphPtr->elemTable), argv[i]);
  972.         if (entryPtr == NULL) {
  973.         Tcl_AppendResult(interp, "can't find element \"", argv[i],
  974.             "\" in \"", argv[0], "\"", (char *)NULL);
  975.         return TCL_ERROR;
  976.         }
  977.         elemPtr = (Element *)Tcl_GetHashValue(entryPtr);
  978.         if (!elemPtr->mapped) {
  979.         Tcl_AppendResult(interp, "element \"", argv[i],
  980.             "\" isn't mapped", (char *)NULL);
  981.         return TCL_ERROR;
  982.         }
  983.         if ((*elemPtr->closestProc) (graphPtr, elemPtr, x, y, &elem)) {
  984.         if (elem.dist < min.dist) {
  985.             min = elem;
  986.         }
  987.         }
  988.     }
  989.     } else {
  990.     Blt_ListEntry *entryPtr;
  991.     /*
  992.      * Find the closest point in set of displayed elements,
  993.      * searching the display list from back to front.  It's
  994.      * possible that two or more element's may include the same
  995.      * closest data point.  In these cases, we pick the one drawn
  996.      * last. BTW. It's ok if an element itself has more than one
  997.      * point which is the closest.  Active elements are drawn
  998.      * after the normal elements.
  999.      */
  1000.     for (entryPtr = Blt_LastListEntry(&(graphPtr->elemList));
  1001.         entryPtr != NULL; entryPtr = Blt_PrevListEntry(entryPtr)) {
  1002.         elemPtr = (Element *)Blt_GetListValue(entryPtr);
  1003.         if ((*elemPtr->closestProc) (graphPtr, elemPtr, x, y, &elem)) {
  1004.         if (elem.dist < min.dist) {
  1005.             min = elem;
  1006.         }
  1007.         }
  1008.     }
  1009.     }
  1010.  
  1011.     if (min.dist != Blt_posInfinity) {
  1012.     char string[TCL_DOUBLE_SPACE + 1];
  1013.  
  1014.     Tcl_AppendElement(interp, min.elemPtr->id);
  1015.     sprintf(string, "%d", min.index);
  1016.     Tcl_AppendElement(interp, string);
  1017.     Tcl_PrintDouble(interp, min.x, string);
  1018.     Tcl_AppendElement(interp, string);
  1019.     Tcl_PrintDouble(interp, min.y, string);
  1020.     Tcl_AppendElement(interp, string);
  1021.     }
  1022.     return TCL_OK;
  1023. }
  1024.  
  1025. /*
  1026.  *----------------------------------------------------------------------
  1027.  *
  1028.  * ActivateElement --
  1029.  *
  1030.  *    Find the element closest to the window coordinates specified.
  1031.  *
  1032.  * Results:
  1033.  *    Returns TCL_OK if no errors occured. The result field of the
  1034.  *    interpreter may contain a list containing the element name,
  1035.  *    the index of the closest point, and the x and y graph coordinates
  1036.  *    of the point is stored.  If an error occurred, returns TCL_ERROR
  1037.  *    and an error message is left in interp->result.
  1038.  *
  1039.  *----------------------------------------------------------------------
  1040.  */
  1041.  
  1042. static int
  1043. ActivateElement(graphPtr, interp, argc, argv)
  1044.     Graph *graphPtr;        /* Graph widget */
  1045.     Tcl_Interp *interp;
  1046.     int argc;            /* Number of element names */
  1047.     char **argv;        /* List of element names */
  1048. {
  1049.     Element *elemPtr;
  1050.     Tcl_HashEntry *entryPtr;
  1051.     int numActive;
  1052.     int *indexArr;
  1053.  
  1054.     if (argc < 4) {
  1055.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  1056.         " element ", argv[2], " name ?index...?\"", (char *)NULL);
  1057.     return TCL_ERROR;
  1058.     }
  1059.     entryPtr = Tcl_FindHashEntry(&(graphPtr->elemTable), argv[3]);
  1060.     if (entryPtr == NULL) {
  1061.     Tcl_AppendResult(interp, "can't find element \"", argv[3], "\" in \"",
  1062.         argv[0], "\"", (char *)NULL);
  1063.     return TCL_ERROR;
  1064.     }
  1065.     elemPtr = (Element *)Tcl_GetHashValue(entryPtr);
  1066.     elemPtr->flags |= ACTIVE;
  1067.     numActive = argc - 4;
  1068.     indexArr = elemPtr->staticArr;
  1069.     if (numActive > 0) {
  1070.     register int i;
  1071.     int index;
  1072.  
  1073.     if (argc > DEF_ACTIVE_SIZE) {
  1074.         indexArr = (int *)malloc(sizeof(int) * numActive);
  1075.     }
  1076.     if (indexArr == NULL) {
  1077.         interp->result = "can't allocate index array";
  1078.         return TCL_ERROR;
  1079.     }
  1080.     argv += 4;
  1081.     for (i = 0; i < numActive; i++) {
  1082.         if (Tcl_GetInt(interp, argv[i], &index) != TCL_OK) {
  1083.         free((char *)indexArr);
  1084.         return TCL_ERROR;
  1085.         }
  1086.         indexArr[i] = index;
  1087.     }
  1088.     }
  1089.     elemPtr->numActivePoints = numActive;
  1090.     if (elemPtr->activeArr != elemPtr->staticArr) {
  1091.     free((char *)elemPtr->activeArr);
  1092.     }
  1093.     elemPtr->activeArr = indexArr;
  1094.     Blt_RedrawGraph(graphPtr);
  1095.     return TCL_OK;
  1096. }
  1097.  
  1098. /*
  1099.  *----------------------------------------------------------------------
  1100.  *
  1101.  * DeactivateElement --
  1102.  *
  1103.  *    Clears the active bit for the named elements.
  1104.  *
  1105.  * Results:
  1106.  *    Returns TCL_OK if no errors occured.
  1107.  *
  1108.  *----------------------------------------------------------------------
  1109.  */
  1110.  
  1111. static int
  1112. DeactivateElement(graphPtr, interp, argc, argv)
  1113.     Graph *graphPtr;        /* Graph widget */
  1114.     Tcl_Interp *interp;
  1115.     int argc;            /* Number of element names */
  1116.     char **argv;        /* List of element names */
  1117. {
  1118.     Element *elemPtr;
  1119.     Tcl_HashEntry *entryPtr;
  1120.     register int i;
  1121.  
  1122.     if (argc < 4) {
  1123.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  1124.         " element deactivate name ?name...?\"", (char *)NULL);
  1125.     return TCL_ERROR;
  1126.     }
  1127.     for (i = 3; i < argc; i++) {
  1128.     entryPtr = Tcl_FindHashEntry(&(graphPtr->elemTable), argv[3]);
  1129.     if (entryPtr == NULL) {
  1130.         Tcl_AppendResult(interp, "can't find element \"", argv[3],
  1131.         "\" in \"", argv[0], "\"", (char *)NULL);
  1132.         return TCL_ERROR;
  1133.     }
  1134.     elemPtr = (Element *)Tcl_GetHashValue(entryPtr);
  1135.     elemPtr->flags &= ~ACTIVE;
  1136.     elemPtr->numActivePoints = 0;
  1137.     if (elemPtr->activeArr != elemPtr->staticArr) {
  1138.         free((char *)elemPtr->activeArr);
  1139.         elemPtr->activeArr = elemPtr->staticArr;
  1140.     }
  1141.     }
  1142.     Blt_RedrawGraph(graphPtr);
  1143.     return TCL_OK;
  1144. }
  1145.  
  1146. /*
  1147.  * Global routines:
  1148.  */
  1149.  
  1150. /*
  1151.  *--------------------------------------------------------------
  1152.  *
  1153.  * Blt_ElementCmd --
  1154.  *
  1155.  *    This procedure is invoked to process the Tcl command
  1156.  *    that corresponds to a widget managed by this module.
  1157.  *    See the user documentation for details on what it does.
  1158.  *
  1159.  * Results:
  1160.  *    A standard Tcl result.
  1161.  *
  1162.  * Side effects:
  1163.  *    See the user documentation.
  1164.  *
  1165.  *--------------------------------------------------------------
  1166.  */
  1167. /* ARGSUSED */
  1168. int
  1169. Blt_ElementCmd(graphPtr, argc, argv)
  1170.     Graph *graphPtr;        /* Graph widget record */
  1171.     int argc;            /* # arguments */
  1172.     char **argv;        /* Argument list */
  1173. {
  1174.     int result = TCL_ERROR;
  1175.     char c;
  1176.     int length;
  1177.     Tcl_Interp *interp = graphPtr->interp;
  1178.  
  1179.     if (argc < 3) {
  1180.     Tcl_AppendResult(graphPtr->interp, "wrong # args: should be \"",
  1181.         argv[0], " element option ?args?\"", NULL);
  1182.     return TCL_ERROR;
  1183.     }
  1184.     c = argv[2][0];
  1185.     length = strlen(argv[2]);
  1186.     if ((c == 'c') && (length > 1) &&
  1187.     (strncmp(argv[2], "create", length) == 0)) {
  1188.     result = CreateElement(graphPtr, interp, argc, argv);
  1189.     } else if ((c == 'c') && (length > 1) &&
  1190.     (strncmp(argv[2], "configure", length) == 0)) {
  1191.     result = ConfigureElement(graphPtr, interp, argc, argv);
  1192.     } else if ((c == 'c') && (length > 1) &&
  1193.     (strncmp(argv[2], "closest", length) == 0)) {
  1194.     result = ClosestElement(graphPtr, interp, argc, argv);
  1195.     } else if ((c == 'a') && (length > 1) &&
  1196.     (strncmp(argv[2], "append", length) == 0)) {
  1197.     result = AppendElement(graphPtr, interp, argc, argv);
  1198.     } else if ((c == 'a') && (length > 1) &&
  1199.     (strncmp(argv[2], "activate", length) == 0)) {
  1200.     result = ActivateElement(graphPtr, interp, argc, argv);
  1201.     } else if ((c == 'd') && (length > 2) &&
  1202.     (strncmp(argv[2], "deactivate", length) == 0)) {
  1203.     result = DeactivateElement(graphPtr, interp, argc, argv);
  1204.     } else if ((c == 'd') && (length > 2) &&
  1205.     (strncmp(argv[2], "delete", length) == 0)) {
  1206.     result = DeleteElements(graphPtr, interp, argc, argv);
  1207.     } else if ((c == 'n') && (strncmp(argv[2], "names", length) == 0)) {
  1208.     result = ElementNames(graphPtr, interp, argc, argv);
  1209.     } else if ((c == 's') && (strncmp(argv[2], "show", length) == 0)) {
  1210.     result = ShowElements(graphPtr, interp, argc, argv);
  1211.     } else {
  1212.     char *options;
  1213.  
  1214.     options = " activate, append, closest, configure, create, \
  1215. deactivate, delete, names, or show";
  1216.     Tcl_AppendResult(graphPtr->interp, "bad element option \"", argv[2],
  1217.         "\": should be ", options, (char *)NULL);
  1218.     return TCL_ERROR;
  1219.     }
  1220.     return result;
  1221. }
  1222.  
  1223. /*
  1224.  *----------------------------------------------------------------------
  1225.  *
  1226.  * UpdateVector --
  1227.  *
  1228.  *    Called by C interface routine Blt_GraphElement.
  1229.  *
  1230.  * Results:
  1231.  *    The return value is a standard Tcl result.  The vector is passed
  1232.  *    back via the vecPtr.
  1233.  *
  1234.  *----------------------------------------------------------------------
  1235.  */
  1236. static int
  1237. UpdateVector(graphPtr, vecPtr, start, numValues, valueArr)
  1238.     Graph *graphPtr;        /* Graph widget */
  1239.     Vector *vecPtr;        /* Element vector */
  1240.     int start;            /* Starting value */
  1241.     int numValues;        /* Number of elements in array */
  1242.     double *valueArr;        /* Array of floating point values */
  1243. {
  1244.     unsigned int arraySize;
  1245.     double *newArr;
  1246.     register int offset;
  1247.     register int i;
  1248.     int needed;
  1249.  
  1250.     offset = vecPtr->length;
  1251.     needed = (numValues / 2) + offset;
  1252.  
  1253.     /*
  1254.      * Keep doubling the size of the array until we fit the number
  1255.      * needed
  1256.      */
  1257.     arraySize = vecPtr->size;
  1258.     if (arraySize == 0) {
  1259.     arraySize = DEF_VECTOR_SIZE;
  1260.     }
  1261.     while (needed > arraySize) {
  1262.     arraySize += arraySize;
  1263.     }
  1264.     newArr = (double *)malloc(arraySize * sizeof(double));
  1265.     if (newArr == NULL) {
  1266.     graphPtr->interp->result = "can't allocate data vector";
  1267.     return TCL_ERROR;
  1268.     }
  1269.     if (offset > 0) {
  1270.     memcpy((char *)newArr, (char *)vecPtr->data, offset * sizeof(double));
  1271.     }
  1272.     /*
  1273.      * Pick out every other value and stuff it into the extended
  1274.      * array.
  1275.      */
  1276.     for (i = start; i < numValues; i += 2) {
  1277.     newArr[offset++] = valueArr[i];
  1278.     }
  1279.     if (vecPtr->data != NULL) {
  1280.     free((char *)vecPtr->data);
  1281.     }
  1282.     vecPtr->data = newArr;
  1283.     vecPtr->length = needed;
  1284.     vecPtr->size = arraySize;
  1285.     GetDataLimits(vecPtr);
  1286.     return TCL_OK;
  1287. }
  1288.  
  1289. /*
  1290.  *----------------------------------------------------------------------
  1291.  *
  1292.  * Blt_GraphElement --
  1293.  *
  1294.  *    User convenience routine to set data of an individual graph
  1295.  *    element.
  1296.  *
  1297.  * Results:
  1298.  *    The return value is a standard Tcl result.
  1299.  *
  1300.  * Side Effects:
  1301.  *      Graph is redrawn with new data values. Axes are recalculated.
  1302.  *
  1303.  *----------------------------------------------------------------------
  1304.  */
  1305. /*LINTLIBRARY*/
  1306. int
  1307. Blt_GraphElement(interp, pathName, elemName, numValues, valueArr)
  1308.     Tcl_Interp *interp;        /* Interpreter to send results back to */
  1309.     char *pathName;        /* Path name of graph widget */
  1310.     char *elemName;        /* Name of element to set */
  1311.     int numValues;        /* Number of coordinates in array */
  1312.     double *valueArr;        /* Array of XY coordinate values */
  1313. {
  1314.     Tcl_HashEntry *entryPtr;
  1315.     Graph *graphPtr;
  1316.     Element *elemPtr;
  1317.     Tk_Window mainWin, tkwin;
  1318.     Tk_Uid classUid;
  1319.     ClientData clientData;
  1320.  
  1321.     mainWin = Tk_MainWindow(interp);
  1322.     if (mainWin == NULL) {
  1323.     return TCL_ERROR;
  1324.     }
  1325.     tkwin = Tk_NameToWindow(interp, pathName, mainWin);
  1326.     if (tkwin == NULL) {
  1327.     return TCL_ERROR;
  1328.     }
  1329.     classUid = Tk_Class(tkwin);
  1330.     if (classUid != Tk_GetUid("Blt_graph")) {
  1331.     Tcl_AppendResult(interp, "window \"", pathName,
  1332.         "\" is the wrong class \"", classUid, "\"", (char *)NULL);
  1333.     return TCL_ERROR;
  1334.     }
  1335.     if (Blt_FindCmd(interp, pathName, &clientData) != TCL_OK) {
  1336.     Tcl_AppendResult(interp, "can't find command \"", pathName,
  1337.         "\"", (char *)NULL);
  1338.     return TCL_ERROR;
  1339.     }
  1340.     if (numValues <= 2) {
  1341.     Tcl_AppendResult(interp, "too few values in array", (char *)NULL);
  1342.     return TCL_ERROR;
  1343.     }
  1344.     if (numValues & 1) {
  1345.     Tcl_AppendResult(interp, "odd number of values in array", (char *)NULL);
  1346.     return TCL_ERROR;
  1347.     }
  1348.     graphPtr = (Graph *)clientData;
  1349.     entryPtr = Tcl_FindHashEntry(&(graphPtr->elemTable), elemName);
  1350.     if (entryPtr == NULL) {
  1351.     Tcl_AppendResult(interp, "can't find element \"", elemName, "\" in \"",
  1352.         pathName, "\"", (char *)NULL);
  1353.     return TCL_ERROR;
  1354.     }
  1355.     elemPtr = (Element *)Tcl_GetHashValue(entryPtr);
  1356.  
  1357.     /* Reset element data values */
  1358.     elemPtr->x.length = elemPtr->y.length = 0;
  1359.     UpdateVector(graphPtr, &(elemPtr->x), 0, numValues, valueArr);
  1360.     UpdateVector(graphPtr, &(elemPtr->y), 1, numValues, valueArr);
  1361.  
  1362.     /* Indicate element layout needs to be recalculated. */
  1363.     elemPtr->flags |= LAYOUT_NEEDED;
  1364.     graphPtr->flags |= DIRTY;
  1365.     Blt_ComputeAxes(graphPtr);
  1366.     Blt_RedrawGraph(graphPtr);
  1367.     return TCL_OK;
  1368. }
  1369.